/***********************************************
 **** 版权声明处 **
 ****  为了方便阅读和维护，请严格遵守相关代码规范，谢谢   ****
 *******************************************/
package serialization
{
	/*******************************************
	 **** huangjixin,2012-10-12,上午11:41:24 **
	 **** UndoManager  **
	 *******************************************/
	import bpm.graphic.DiagramEditor;
	
	import com.hnisi.Diagram;
	import com.hnisi.Graph;
	import com.hnisi.events.DiagramEditorEvent;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import mx.core.UIComponent;
	import mx.events.SandboxMouseEvent;

	/**
	 * The UndoManager class implements a simple undo/redo mechanism for a DiagramEditor.
	 * 
	 * The undo manager must be attached to a DiagramEditor instance by setting the editor property.
	 * The graph is serialized (using an XMLSerializer object) when changes
	 * have been done in the editor. The undo() and redo() methods restore the previous
	 * state by deserializing the serialized graph.
	 */
	public class HnisiUndoManager extends UIComponent
	{
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// private 类私有静态变量和静态常量声明处。（全部大写，使用下划线进行分割）
		// 例如：private static const EXAMPLE:String = "example";
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// public 类公有静态变量和静态常量声明处。（全部大写，使用下划线进行分割）
		// 例如：public static const EXAMPLE:String = "example";
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// private 私有变量声明处，请以“_”开头定义变量
		// 例如：private var _example:String;
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// The diagram editor to which to undo manager is attached
		private var _editor:Diagram;
		
		// Contains the successive states of the graph.
		private var _states:Vector.<Object> = new Vector.<Object>();
		
		// Index of the current state.
		private var _currentState:int = -1;
		
		// Was the graph modified since the last state change?
		private var _modified:Boolean = false;
		
		private var _mouseDown:Boolean = false;
		
		private var _undoEnabled:Boolean = true;
		
		private var _undoLimit:uint = 100;
		
		protected var _serializer:HnisiXMLSerializer = new HnisiXMLSerializer();
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// public 公有变量声明处
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		[Bindable]
		/**
		 * Indicates whether any changes can be undone.
		 * 
		 * @see #undo()
		 */
		public var canUndo:Boolean = false;
		
		[Bindable]
		/**
		 * Indicates whether any undone changes can be redone.
		 * 
		 * @see #redo()
		 */
		public var canRedo:Boolean = false;
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// 构造函数，初始化相关工作可以放在里面
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		public function HnisiUndoManager()
		{
			super();
		}//构造函数结束
		
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// getter和setter函数
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		/**
		 * The Graph instance being modified. 
		 */
		public function get graph(): Graph
		{
			return _editor.graph;
		}
		
		public function get editor():DiagramEditor
		{
			return _editor;
		}
		
		public function set editor(value:DiagramEditor):void
		{
			if(_editor != null){
				editor.removeEventListener("enterFrame", enterFrameHandler);
				editor.removeEventListener(DiagramEditorEvent.EDITOR_CREATE, handleEventForUndo);
				editor.removeEventListener(DiagramEditorEvent.EDITOR_DELETE, handleEventForUndo);
				editor.removeEventListener(DiagramEditorEvent.EDITOR_MOVE, handleEventForUndo);
				editor.removeEventListener(DiagramEditorEvent.EDITOR_RESIZE, handleEventForUndo);
				editor.removeEventListener(DiagramEditorEvent.EDITOR_REPARENT, handleEventForUndo);
				/*editor.removeEventListener(DiagramEditorEvent.EDITOR_CONNECT_LINK, handleEventForUndo);
				editor.removeEventListener(DiagramEditorEvent.EDITOR_CHANGE_ZORDER, handleEventForUndo);*/
				editor.removeEventListener(DiagramEditorEvent.EDITOR_FINISH_TEXT_EDITING, handleEventForUndo);
				editor.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
				/*editor.removeEventListener(SubgraphEvent.COLLAPSE_ANIMATION_END, handleSubgraphExpandCollapse);
				editor.removeEventListener(SubgraphEvent.EXPAND_ANIMATION_END, handleSubgraphExpandCollapse);
				editor.removeEventListener(GraphEvent.GRAPH_LAYOUT_END, handleGraphLayoutEnd);*/
			}
			
			_editor = value;
			
			if(_editor != null){
				editor.addEventListener("enterFrame", enterFrameHandler);
				editor.addEventListener(DiagramEditorEvent.EDITOR_CREATE, handleEventForUndo);
				editor.addEventListener(DiagramEditorEvent.EDITOR_DELETE, handleEventForUndo);
				editor.addEventListener(DiagramEditorEvent.EDITOR_MOVE, handleEventForUndo);
				editor.addEventListener(DiagramEditorEvent.EDITOR_RESIZE, handleEventForUndo);
				editor.addEventListener(DiagramEditorEvent.EDITOR_REPARENT, handleEventForUndo);
				/*editor.addEventListener(DiagramEditorEvent.EDITOR_CONNECT_LINK, handleEventForUndo);
				editor.addEventListener(DiagramEditorEvent.EDITOR_CHANGE_ZORDER, handleEventForUndo);*/
				editor.addEventListener(DiagramEditorEvent.EDITOR_FINISH_TEXT_EDITING, handleEventForUndo);
				editor.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler, true);
				/*editor.addEventListener(SubgraphEvent.COLLAPSE_ANIMATION_END, handleSubgraphExpandCollapse);
				editor.addEventListener(SubgraphEvent.EXPAND_ANIMATION_END, handleSubgraphExpandCollapse);
				editor.addEventListener(GraphEvent.GRAPH_LAYOUT_END, handleGraphLayoutEnd);*/
				_modified = true;
			}
		}		
		
		[Bindable]
		/**
		 * Enables or disables undo/redo for this editor.
		 */
		public function get undoEnabled() : Boolean
		{
			return _undoEnabled;
		}
		
		/**
		 * @private
		 */
		public function set undoEnabled(enabled:Boolean) : void
		{
			_undoEnabled = enabled;
			if(enabled)
				updateFlags();
			else
				clearUndo();
		}
		
		/**
		 * The maximum number of changes that can be undone.
		 */
		public function get undoLimit() : uint
		{
			return _undoLimit;
		}
		
		/**
		 * @private
		 */
		public function set undoLimit(limit:uint) : void
		{
			_undoLimit = limit;
			if(_states.length > _undoLimit){
				_currentState -= _states.length - _undoLimit;
				if(_currentState < -1)
					_currentState = -1;
				_states.splice(0, _states.length - _undoLimit);
			}
		}
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// 相关事件响应函数和逻辑函数存放处
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		private function mouseDownHandler(event:MouseEvent) : void
		{
			_mouseDown = true;
			
			var sandboxRoot:DisplayObject = _editor.systemManager.getSandboxRoot();
			sandboxRoot.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
			sandboxRoot.addEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseUpHandler, true);
		}
		
		private function mouseUpHandler(event:MouseEvent) : void
		{
			_mouseDown = false;
			
			var sandboxRoot:DisplayObject = _editor.systemManager.getSandboxRoot();
			sandboxRoot.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
			sandboxRoot.removeEventListener(SandboxMouseEvent.MOUSE_UP_SOMEWHERE, mouseUpHandler, true);
		}
		
		/**
		 * Undoes the last changes in the editor.
		 * 
		 * @see #redo()
		 * @see #canUndo
		 * @see #canRedo
		 */
		public function undo() : void
		{
			if(canUndo){
				_editor.clear();
				
				_currentState--;
				deserialize(_states[_currentState]);
				_modified = false;
				
				updateFlags();
			}
		}
		
		/**
		 * Redoes the last undone changes in the editor.
		 * 
		 * @see #undo()
		 * @see #canUndo
		 * @see #canRedo
		 */
		public function redo() : void
		{
			if(canRedo){
				_editor.clear();
				
				_currentState++;
				deserialize(_states[_currentState]);
				_modified = false;
				
				updateFlags();
			}
		}
		private function updateFlags() : void
		{
			canUndo = _currentState > 0;
			canRedo = _currentState < _states.length - 1;
		} 
		/**
		 * Clears the undo stack.
		 */
		public function clearUndo() : void
		{
			_states = new Vector.<Object>();
			_currentState = -1;
			_modified = true;
			
			updateFlags();
		}
		
		/**
		 * Causes the editor to record its state so that a change can be undone.
		 * This function should be called after external changes to the graph have been done
		 * (like performing a graph layout for example), to allow the changes to be undone.
		 */
		public function recordUndo() : void
		{
			_editor.validateNow();
			_modified = true;
		}
		
		private function enterFrameHandler(event:Event) : void 
		{
			if(!_mouseDown && _modified && _undoEnabled){
				
				_currentState++;
				_states.splice(_currentState, _states.length - _currentState, serialize());
				if(_states.length > _undoLimit){
					_currentState -= _states.length - _undoLimit;
					if(_currentState < -1)
						_currentState = -1;
					_states.splice(0, _states.length - _undoLimit);
				}
				_modified = false;
				
				updateFlags();
			}
		}
		
		// Called whenever something changes in the editor
		private function handleEventForUndo(event:DiagramEditorEvent) : void
		{
			if(event.isDefaultPrevented())
				return;
			
			if(event.type == DiagramEditorEvent.EDITOR_FINISH_TEXT_EDITING && event.textEditCancelled)
				return;
			
			_modified = true;
		}
		
		protected function serialize() : Object
		{
			var xml:XML = _serializer.serialize(_editor.graph, "undo");
			return xml;
		}
		
		protected function deserialize(serialized:Object) : void
		{
			_serializer.deserialize(_editor.graph, XML(serialized)); 
		}
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// override 覆盖函数
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/


	}//类结束
}//包结束